#include <QtCore/QDateTime> // for QDateTime
#include <QtCore/QFile> // for QFile
#include <QtCore/QList> // for QList<>::iterator, QList
+#include <QtCore/QRegExp> // for QRegExp
#include <QtCore/QString> // for QString
#include <QtCore/QTextCodec> // for QTextCodec
#include <QtCore/QTime> // for QTime
#include <QtCore/QVariant> // for QVariant
#include <QtCore/QVector> // for QVector
-#include <QtCore/Qt> // for ISODate, UTC
+#include <QtCore/Qt> // for UTC, ISODate
#include <QtCore/QtGlobal> // for qPrintable
#include <algorithm> // for sort, min
#include <cassert> // for assert
#include <cstdio> // for printf, snprintf, SEEK_SET, SEEK_CUR
#include <cstdlib> // for labs, atoi
#include <cstring> // for strrchr, memcmp, strchr, strlen
-#include <ctime> // for gmtime, localtime, time_t, tm
#include <ctype.h> // for isprint, isspace
#define MYNAME "exif"
static QList<ExifApp>* exif_apps;
static ExifApp* exif_app;
static const Waypoint* exif_wpt_ref;
-static time_t exif_time_ref;
+static QDateTime exif_time_ref;
static char exif_success;
static QString exif_fout_name;
return size;
}
-// TODO: If this were actually ever used (!?!?!) it could probably be
-// replaced by return QDateTime(time).toString("yyyy/MM/dd, hh:mm:ss);
static QString
-exif_time_str(const time_t time)
+exif_time_str(const QDateTime& time)
{
- struct tm tm = *localtime(&time);
- tm.tm_year += 1900;
- tm.tm_mon += 1;
-
- return QString().sprintf("%04d/%02d/%02d, %02d:%02d:%02d",
- tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+ QString str = time.toString("yyyy/MM/dd hh:mm:ss t");
+ if (time.timeSpec() != Qt::UTC) {
+ str.append(" (");
+ str.append(time.toUTC().toString("yyyy/MM/dd hh:mm:ss t"));
+ str.append(")");
+ }
+ return str;
}
static char*
return nullptr;
}
-static time_t
+static QDateTime
exif_get_exif_time(ExifApp* app)
{
QDateTime res;
if (tag) {
char* str = exif_read_str(tag);
+ // This assumes the Qt::TimeSpec is Qt::LocalTime, i.e. the current system time zone.
+ // Note the assumption of local time can be problematic if the data
+ // is processed in a different time zone than was used in recording
+ // the time in the image.
res = QDateTime::fromString(str, "yyyy:MM:dd hh:mm:ss");
xfree(str);
+
+ // Exif 2.31 added offset tags to record the offset to UTC.
+ // If these are present use them, otherwise assume local time.
+ ExifTag* offset_tag = nullptr;
+ switch (tag->id) {
+ case 0x9003:
+ offset_tag = exif_find_tag(app, EXIF_IFD, 0x9011); /* OffsetTimeOriginal from EXIF */
+ break;
+ case 0x0132:
+ offset_tag = exif_find_tag(app, EXIF_IFD, 0x9010); /* OffsetTime from EXIF */
+ break;
+ case 0x9004:
+ offset_tag = exif_find_tag(app, EXIF_IFD, 0x9012); /* OffsetTimeDigitized from EXIF */
+ break;
+ }
+
+ if (offset_tag) {
+ char* str = exif_read_str(offset_tag);
+ // string should be +HH:MM or -HH:MM
+ QRegExp re("([+-])(\\d{2})(?::)(\\d{2})");
+ if (re.exactMatch(str)) {
+ // Correct the date time by supplying the offset from UTC.
+ int offset_hours = re.cap(1).append(re.cap(2)).toInt();
+ int offset_mins = re.cap(1).append(re.cap(3)).toInt();
+ res.setOffsetFromUtc(((offset_hours * 60) + offset_mins) * 60);
+ }
+ }
}
- return res.toTime_t();
+ return res;
}
static Waypoint*
if (exif_wpt_ref == nullptr) {
exif_wpt_ref = wpt;
- } else if (labs(exif_time_ref - wpt->creation_time.toTime_t()) < labs(exif_time_ref - exif_wpt_ref->creation_time.toTime_t())) {
+ } else if (labs(exif_time_ref.msecsTo(wpt->creation_time)) < labs(exif_time_ref.msecsTo(exif_wpt_ref->creation_time))) {
exif_wpt_ref = wpt;
}
}
gbfclose(fin);
exif_time_ref = exif_get_exif_time(exif_app);
- if (exif_time_ref == 0) {
+ if (!exif_time_ref.isValid()) {
fatal(MYNAME ": No valid timestamp found in picture!\n");
}
warning(MYNAME ": No matching point with name \"%s\" found.\n", opt_name);
}
} else {
- QString str = exif_time_str(exif_time_ref);
-
track_disp_all(nullptr, nullptr, exif_find_wpt_by_time);
route_disp_all(nullptr, nullptr, exif_find_wpt_by_time);
waypt_disp_all(exif_find_wpt_by_time);
- time_t frame = atoi(opt_frame);
+ qint64 frame = atoi(opt_frame);
if (exif_wpt_ref == nullptr) {
warning(MYNAME ": No point with a valid timestamp found.\n");
- } else if (labs(exif_time_ref - exif_wpt_ref->creation_time.toTime_t()) > frame) {
+ } else if (labs(exif_time_ref.secsTo(exif_wpt_ref->creation_time)) > frame) {
+ QString str = exif_time_str(exif_time_ref);
warning(MYNAME ": No matching point found for image date %s!\n", qPrintable(str));
if (exif_wpt_ref != nullptr) {
- QString str = exif_time_str(exif_wpt_ref->creation_time.toTime_t());
+ QString str = exif_time_str(exif_wpt_ref->creation_time);
warning(MYNAME ": Best is from %s, %ld second(s) away.\n",
- qPrintable(str), labs(exif_time_ref - exif_wpt_ref->creation_time.toTime_t()));
+ qPrintable(str), labs(exif_time_ref.secsTo(exif_wpt_ref->creation_time)));
}
exif_wpt_ref = nullptr;
}
}
if (wpt->creation_time.isValid()) {
- char buf[32];
-
- const time_t tt = wpt->GetCreationTime().toTime_t();
- struct tm tm = *gmtime(&tt);
-
- tm.tm_year += 1900;
- tm.tm_mon += 1;
+ const QDateTime dt = wpt->GetCreationTime().toUTC();
- exif_put_double(GPS_IFD, GPS_IFD_TAG_TIMESTAMP, 0, tm.tm_hour);
- exif_put_double(GPS_IFD, GPS_IFD_TAG_TIMESTAMP, 1, tm.tm_min);
- exif_put_double(GPS_IFD, GPS_IFD_TAG_TIMESTAMP, 2, tm.tm_sec);
+ exif_put_double(GPS_IFD, GPS_IFD_TAG_TIMESTAMP, 0, dt.time().hour());
+ exif_put_double(GPS_IFD, GPS_IFD_TAG_TIMESTAMP, 1, dt.time().minute());
+ exif_put_double(GPS_IFD, GPS_IFD_TAG_TIMESTAMP, 2, dt.time().second());
- snprintf(buf, sizeof(buf), "%04d:%02d:%02d", tm.tm_year, tm.tm_mon, tm.tm_mday);
- exif_put_str(GPS_IFD, GPS_IFD_TAG_DATESTAMP, buf);
+ exif_put_str(GPS_IFD, GPS_IFD_TAG_DATESTAMP, CSTR(dt.toString("yyyy:MM:dd")));
} else {
exif_remove_tag(GPS_IFD, GPS_IFD_TAG_TIMESTAMP);
exif_remove_tag(GPS_IFD, GPS_IFD_TAG_DATESTAMP);